// SPDX-License-Identifier: LGPL-2.1+
/*
* Tiny printf version for SPL
*
* Copied from:
* http://www.sparetimelabs.com/printfrevisited/printfrevisited.php
*
* Copyright (C) 2004,2008  Kustaa Nyholm
*/

#include "stdio.h"
#include "stdint.h"
#include "string.h"
#include "honrun_printf.h"





int8_t cHonrunItoA(char *pcBuff, int64_t iValue, int8_t cBase, int8_t cWidthLimit, int8_t cLeadingZero, int8_t cLeft)
{
    int64_t iDiv = 1;
    int8_t cNumber = 0, cLength = 1, cENOB = 0;

    /* 计算真实宽度 */
    while ((iValue / iDiv) >= cBase)
    {
        iDiv *= cBase;
        ++cLength;
    }

    /* 右对齐 */
    if(cLeft == 0)
    {
        /* 补充到最低宽度 */
        while(cLength < cWidthLimit)
        {
            iDiv *= cBase;
            ++cLength;
        }
    }

    /* 转换成 strings */
    if(pcBuff != NULL)
    {
        while (iDiv != 0)
        {
            cNumber = iValue / iDiv;

            /* 是否是无意义的 0 */
            cENOB = (cNumber != 0) ? 1 : cENOB;

            if(cENOB != 0)
            {
                *pcBuff++ = (cNumber > 9) ? ((cNumber - 10) + 'A') : (cNumber + '0');
            }
            /* 前导对齐 */
            else
            {
                *pcBuff++ = (cLeadingZero != 0) ? '0' : ' ';
            }

            iValue %= iDiv;
            iDiv /= cBase;
        }
    }

    /* 左对齐 */
    if(cLeft != 0)
    {
        /* 补充到最低宽度 */
        while(cLength < cWidthLimit)
        {
            if(pcBuff != NULL)
            {
                *pcBuff++ = ' ';
            }
            ++cLength;
        }
    }

    return cLength;
}

int64_t iHonrunAtoI(char *pcBuff, int8_t cBase)
{
    int64_t iValue = 0;
    int8_t cNumber = 0;
    char ch = 0;

    if(pcBuff != NULL)
    {
        while((ch = *pcBuff++) != 0)
        {
            iValue *= cBase;

            if((ch >= '0') && (ch <= '9'))
                cNumber = ch - '0';
            else if((ch >= 'A') && (ch <= 'Z'))
                cNumber = ch - 'A' + 10;
            else if((ch >= 'a') && (ch <= 'z'))
                cNumber = ch - 'a' + 10;

            iValue += cNumber;
        }
    }

    return iValue;
}

static void out(struct printf_info *info, char c)
{
    *info->bf++ = c;
}

static void out_dgt(struct printf_info *info, char dgt)
{
    out(info, dgt + (dgt < 10 ? '0' : 'a' - 10));
    info->zs = 1;
}

static void div_out(struct printf_info *info, unsigned long *num, unsigned long div)
{
    unsigned char dgt = 0;

    while (*num >= div) {
        *num -= div;
        dgt++;
    }

    if (info->zs || dgt > 0)
        out_dgt(info, dgt);
}

int32_t honrunVprintfLength(const char *fmt, honrun_va_list va)
{
    uint32_t uiValue = 0, uiLength = 0;
    int8_t cBase = 0, cShort = 0, cLong = 0, cWidthLimit = 0, cLeadingZero = 0, cLeft = 0, cNegative = 0;
    char ch = 0, *pcString = NULL;

    while((ch = *fmt++) != 0)
    {
        if(ch == '%')
        {
            ch = *fmt++;

            cBase = 0, cShort = 0, cLong = 0, cWidthLimit = 0, cLeft = 0, cNegative = 0;

            /* 格式的前导 */
            switch(ch)
            {
                case 'h' : cShort = 1; ch = *fmt++; break;  /* 短整型 */
                case 'l' : cLong = 1;  ch = *fmt++; break;  /* 长整型、 double */
                case '-' : cLeft = 1;  ch = *fmt++; break;  /* 左对齐 */

                default : break;
            }
            /* 格式的前导 */
            cLeadingZero = (ch == '0') ? 1 : 0;
            while((ch >= '0') && (ch <= '9'))
            {
                cWidthLimit = (cWidthLimit * 10) + (ch - '0');

                ch = *fmt++;
            }

            switch(ch)
            {
                case '%' :
                    ++uiLength;
                    break;

                case 'c' :
                    __honrun_va_arg(va, uint32_t);
                    ++uiLength;
                    break;

                case 's' :
                    pcString = __honrun_va_arg(va, char *);
                    while (*pcString++ != 0)
                    {
                        ++uiLength;
                    }
                    break;

                case 'o' :
                    cBase = (cBase == 0) ? 8 : cBase;

                /* 无符号 */
                case 'i' :
                case 'u' :
                    cBase = (cBase == 0) ? 10 : cBase;

                /* 有符号 */
                case 'd' :
                    cNegative = (cBase == 0) ? 1 : cNegative;
                    cBase     = (cBase == 0) ? 10 : cBase;

                /* 以16进制形式，输出指针的值 */
                case 'p' :
                    cWidthLimit = (cBase == 0) ? (sizeof(uint32_t) * 2) : cWidthLimit;

                case '#' :
                    if(cBase == 0)
                    {
                        uiLength += 2;
                        fmt += ((*fmt == 'x') || (*fmt == 'X')) ? 1 : 0;
                    }

                case 'x' :
                case 'X' :
                    cBase = (cBase == 0) ? 16 : cBase;
                    uiValue = __honrun_va_arg(va, uint32_t);

                    if(cNegative != 0)
                    {
                        if((int32_t)uiValue < 0)
                        {
                            ++uiLength;
                            uiValue = 0 - (int32_t)uiValue;
                        }
                    }

                    uiLength += cHonrunItoA(NULL, uiValue, cBase, cWidthLimit, cLeadingZero, cLeft);
                    break;

                default : return 0;
            }
        }
        else
        {
            ++uiLength;
        }
    }

    return uiLength;
}

int32_t honrunVprintfString(printfTypeDef *ptypeHandle, const char *fmt, honrun_va_list va)
{
    uint32_t uiValue = 0;
    int8_t cBase = 0, cShort = 0, cLong = 0, cWidthLimit = 0, cLeadingZero = 0, cLeft = 0, cNegative = 0;
    char cBuff[24] = {0}, ch = 0, *pcString = NULL;

    /* 输出到指定缓存 */
    if((ptypeHandle == NULL) || (ptypeHandle->cPutChar == NULL) || (ptypeHandle->cPutStrings == NULL))
    {
        return 1;
    }

    while((ch = *fmt++) != 0)
    {
        if(ch == '%')
        {
            ch = *fmt++;

            cBase = 0, cShort = 0, cLong = 0, cWidthLimit = 0, cLeadingZero = 0, cLeft = 0, cNegative = 0;

            /* 格式的前导 */
            switch(ch)
            {
                case 'h' : cShort = 1; ch = *fmt++; break;  /* 短整型 */
                case 'l' : cLong = 1;  ch = *fmt++; break;  /* 长整型、 double */
                case '-' : cLeft = 1;  ch = *fmt++; break;  /* 左对齐 */

                default : break;
            }
            /* 格式的前导 */
            cLeadingZero = (ch == '0') ? 1 : 0;
            while((ch >= '0') && (ch <= '9'))
            {
                cWidthLimit = (cWidthLimit * 10) + (ch - '0');

                ch = *fmt++;
            }

            switch(ch)
            {
                case '%' :
                    ptypeHandle->cPutChar(ptypeHandle, ch);
                    break;

                case 'c' :
                    uiValue = __honrun_va_arg(va, uint32_t);
                    ptypeHandle->cPutChar(ptypeHandle, uiValue);
                    break;

                case 's' :
                    pcString = __honrun_va_arg(va, char *);
                    ptypeHandle->cPutStrings(ptypeHandle, pcString);
                    break;

                case 'o' :
                    cBase = (cBase == 0) ? 8 : cBase;

                /* 无符号 */
                case 'i' :
                case 'u' :
                    cBase = (cBase == 0) ? 10 : cBase;

                /* 有符号 */
                case 'd' :
                    cNegative = (cBase == 0) ? 1 : cNegative;
                    cBase     = (cBase == 0) ? 10 : cBase;

                case '#' :
                    if(cBase == 0)
                    {
                        ptypeHandle->cPutStrings(ptypeHandle, "0x");
                        fmt += ((*fmt == 'x') || (*fmt == 'X')) ? 1 : 0;
                    }

                /* 以16进制形式，输出指针的值 */
                case 'p' :
                    cWidthLimit  = (cBase == 0) ? (sizeof(uint32_t) * 2) : cWidthLimit;
                    cLeadingZero = (cBase == 0) ? 1 : cLeadingZero;
                    cLeft        = (cBase == 0) ? 0 : cLeft;

                case 'x' :
                case 'X' :
                    cBase = (cBase == 0) ? 16 : cBase;

                    uiValue = __honrun_va_arg(va, uint32_t);

                    if(cNegative != 0)
                    {
                        if((int32_t)uiValue < 0)
                        {
                            ptypeHandle->cPutChar(ptypeHandle, '-');
                            uiValue = 0 - (int32_t)uiValue;
                        }
                    }

                    memset(cBuff, 0, sizeof(cBuff));
                    cHonrunItoA(cBuff, uiValue, cBase, cWidthLimit, cLeadingZero, cLeft);

                    ptypeHandle->cPutStrings(ptypeHandle, cBuff);
                    break;

                default : return 1;
            }
        }
        else
        {
            ptypeHandle->cPutChar(ptypeHandle, ch);
        }
    }

    return 0;
}

static int  honrunVprintf(struct printf_info *info, const char *fmt, honrun_va_list va)
{
    char ch;
    char *p;
    unsigned long num;
    char buf[12];
    unsigned long div;

    while ((ch = *(fmt++)))
    {
        if (ch != '%')
        {
            info->putc(info, ch);
        }
        else
        {
            int8_t lz = 0, islong = 0;
            int width = 0;

            ch = *(fmt++);
            if (ch == '-')
                ch = *(fmt++);

            if (ch == '0')
            {
                ch = *(fmt++);
                lz = 1;
            }

            if (ch >= '0' && ch <= '9')
            {
                width = 0;
                while (ch >= '0' && ch <= '9')
                {
                    width = (width * 10) + ch - '0';
                    ch = *fmt++;
                }
            }
            if (ch == 'l')
            {
                ch = *(fmt++);
                islong = 1;
            }

            info->bf = buf;
            p = info->bf;
            info->zs = 0;

            switch (ch)
            {
                case '\0':
                    goto abort;
                case 'u':
                case 'd':
                case 'i':
                    div = 1000000000;
                    if (islong)
                    {
                        num = __honrun_va_arg(va, unsigned long);
                        if (sizeof(long) > 4)
                            div *= div * 10;
                    }
                    else
                    {
                        num = __honrun_va_arg(va, unsigned int);
                    }

                    if (ch != 'u')
                    {
                        if (islong && (long)num < 0)
                        {
                            num = -(long)num;
                            out(info, '-');
                        }
                        else if (!islong && (int)num < 0)
                        {
                            num = -(int)num;
                            out(info, '-');
                        }
                    }

                    if (!num)
                    {
                        out_dgt(info, 0);
                    }
                    else
                    {
                        for (; div; div /= 10)
                            div_out(info, &num, div);
                    }
                    break;

                case 'p':
                    islong = 1;
                    /* no break */

                case 'x':
                    if (islong)
                    {
                        num = __honrun_va_arg(va, unsigned long);
                        div = 1UL << (sizeof(long) * 8 - 4);
                    }
                    else
                    {
                        num = __honrun_va_arg(va, unsigned int);
                        div = 0x10000000;
                    }

                    if (!num)
                    {
                        out_dgt(info, 0);
                    }
                    else
                    {
                        for (; div; div /= 0x10)
                            div_out(info, &num, div);
                    }
                    break;

                case 'c':
                    out(info, (char)(__honrun_va_arg(va, int)));
                    break;

                case 's':
                    p = __honrun_va_arg(va, char*);
                    break;

                case '%':
                    out(info, '%');
                default:
                    break;
            }

            *info->bf = 0;
            info->bf = p;
            while (*info->bf++ && width > 0)
                width--;

            while (width-- > 0)
                info->putc(info, lz ? '0' : ' ');

            if (p)
            {
                while ((ch = *p++))
                    info->putc(info, ch);
            }
        }
    }

abort:
    return 0;
}




/* 输出 单个字符 回调函数 */
int8_t cHonrunPutChar(struct printfStruct *ptypeHandle, char ch)
{
    if(ptypeHandle->stringsHead != NULL)
    {
        *ptypeHandle->stringsNow++ = ch;
    }

    return 0;
}

/* 输出 多个字符 回调函数 */
int8_t cHonrunPutStrings(struct printfStruct *ptypeHandle, char *pcStrings)
{
    if(ptypeHandle->stringsHead != NULL)
    {
        while(*pcStrings != 0)
        {
            *ptypeHandle->stringsNow++ = *pcStrings++;
        }
    }

    return 0;
}

int32_t honrunSprintfString(const char *fmt, ...)
{
    printfTypeDef typePrintf = {0};
    honrun_va_list va;
    int32_t iLength = 0;
    char buff[256] = {0};

    __honrun_va_start(va, fmt);
    iLength = honrunVprintfLength(fmt, va);
    __honrun_va_end(va);

    printf("honrunSprintfLength iLength: %d\r\n", iLength);



    typePrintf.cPutChar    = cHonrunPutChar;
    typePrintf.cPutStrings = cHonrunPutStrings;
    typePrintf.stringsHead = buff;
    typePrintf.stringsNow  = buff;

    __honrun_va_start(va, fmt);
    honrunVprintfString(&typePrintf, fmt, va);
    __honrun_va_end(va);

    printf("honrunSprintfString buff: %s\r\n", typePrintf.stringsHead);

    return iLength;
}
